﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Management;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Interop;
using System.Windows.Media;
using Microsoft.NetMicroFramework.Tools.MFDeployTool.Engine;
using Microsoft.Win32;
using System.Diagnostics;

namespace FEZFirmwareUpdater
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow
    {
        // HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\.NETMicroFramework\v4.1\AssemblyFoldersEx\GHI NETMF v4.1 SDK
        private const int WM_DEVICECHANGE = 0x0219;
        private const int DBT_DEVNODES_CHANGED = 0x0007;

        private BackgroundWorker backgroundWorker;
        private bool canClose = true;

        private bool fezDeviceFound;
        private bool firmwareFileFound;

        private MFDeploy mfDeploy = new MFDeploy();
        private Version solutionVersion;

        public MainWindow()
        {
            this.InitializeComponent();
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            NativeHelper.ExtendFrameIntoClientArea(this, -1, -1, -1, -1);

            this.Title += " v" + typeof(MainWindow).Assembly.GetName().Version.ToString(2) + " Alpha";

            this.RegisterDeviceChangeEvent();

            this.RefreshPortList();
            this.SetDefaultFirmwareFile();
        }

        private void RegisterDeviceChangeEvent()
        {
            HwndSource source = HwndSource.FromHwnd(new WindowInteropHelper(this).Handle);
            if (source != null) source.AddHook(this.WndProc);
        }


        private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam, ref bool handled)
        {
            if (msg == WM_DEVICECHANGE && wparam == (IntPtr)DBT_DEVNODES_CHANGED)
            {
                this.RefreshPortList();
            }

            const int WM_NCHITTEST = 0x0084;
            const int HTCAPTION = 0x0002;
            if (msg == WM_NCHITTEST)
            {
                int pos = lparam.ToInt32();
                short yPos = (short)(pos >> 16);
                short xPos = (short)pos;

                Point border1Point = this.border1.TranslatePoint(new Point(0, 0), this);

                Point point = this.PointFromScreen(new Point(xPos, yPos));

                if (point.Y < border1Point.Y)
                {
                    handled = true;
                    return new IntPtr(HTCAPTION);
                }
            }

            return IntPtr.Zero;
        }

        private void SetDefaultFirmwareFile()
        {
            this.firmwareFile.Text = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles) +
                                     @"\GHI Electronics\GHI NETMF v4.1 SDK\USBizi\Firmware\USBizi_CLR.GHI";

            this.CheckFirmwareFile();
        }

        private void CheckFirmwareFile()
        {
            if (File.Exists(this.firmwareFile.Text))
            {
                this.firmwareFileFound = true;
                this.firmwareFileStatus.Text = "Firmware file found.";
            }
            else
            {
                this.firmwareFileFound = false;
                this.firmwareFileStatus.Text = "Firmware file not found.";
            }
            this.UpdateStatuses();
        }

        private void RefreshPortList()
        {
            RefreshMFDeviceList();
            RefreshComPorts();
        }


        private void RefreshMFDeviceList()
        {
            ReadOnlyCollection<MFPortDefinition> mfPorts = this.mfDeploy.EnumPorts(TransportType.USB);
            if (mfPorts.Count > 0)
            {
                using (MFDevice mfDevice = this.mfDeploy.Connect(mfPorts[0]))
                {
                    this.solutionVersion = mfDevice.Engine.Capabilities.SolutionReleaseInfo.solutionVersion;
                }
                this.UpdateStatuses();
            }
        }

        private void RefreshComPorts()
        {
            IDictionary<string, string> portNames = new Dictionary<string, string>();

            var managementScope = new ManagementScope();
            var objectQuery = new ObjectQuery("SELECT DeviceID, Description FROM Win32_SerialPort");
            using (var managementObjectSearcher = new ManagementObjectSearcher(managementScope, objectQuery))
            {
                using (ManagementObjectCollection managementObjectCollection = managementObjectSearcher.Get())
                {
                    foreach (ManagementBaseObject managementObject in managementObjectCollection)
                    {
                        var deviceId = (string)managementObject["DeviceID"];

                        portNames[deviceId] = deviceId + " : " + managementObject["Description"];

                        managementObject.Dispose();
                    }
                }
            }

            this.ports.ItemsSource = portNames;

            if (this.ports.SelectedIndex > 0) return;

            foreach (var pair in portNames)
            {
                if (pair.Value.Contains("GHI Boot Loader"))
                {
                    this.ports.SelectedValue = pair.Key;
                    break;
                }
            }
        }

        private void PortsSelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            string port = null;
            if (e.AddedItems.Count > 0)
            {
                var pair = (KeyValuePair<string, string>)e.AddedItems[0];
                port = pair.Key;
            }

            if (!string.IsNullOrEmpty(port))
            {
                string version = Updater.CheckForFEZDevice(port);
                if (version == null)
                {
                    this.portStatus.Text = "Not a FEZ device or the FEZ device isn't in firmware update mode";
                    this.fezDeviceFound = false;
                }
                else
                {
                    this.portStatus.Text = "FEZ device found. Boot Loader v" + version;
                    this.fezDeviceFound = true;
                }
            }
            else
            {
                this.portStatus.Text = "Please chose a serial port";
                this.fezDeviceFound = false;
            }

            this.UpdateStatuses();
        }

        private void UpdateStatuses()
        {
            this.deviceStatus.Text = !this.fezDeviceFound
                                         ? "To update your FEZ first reset it while holding the LDR button down"
                                         : "Your FEZ is ready to be updated.";

            this.availableVersion.Text = !this.firmwareFileFound
                                             ? "Firmware file not found"
                                             : "unknown";

            this.deviceVersion.Text = solutionVersion == null ? "unknown" : solutionVersion.ToString();

            this.start.IsEnabled = this.fezDeviceFound && this.firmwareFileFound;
        }

        private void FirmwareFileTextChanged(object sender, TextChangedEventArgs e)
        {
            this.CheckFirmwareFile();
        }

        private void StartClick(object sender, RoutedEventArgs e)
        {
            this.IsEnabled = false;
            this.canClose = false;
            this.progress.Value = 0;
            this.progress.Visibility = Visibility.Visible;

            var portName = this.ports.SelectedValue as string;
            string filePath = this.firmwareFile.Text;

            this.backgroundWorker = new BackgroundWorker { WorkerReportsProgress = true };
            this.backgroundWorker.DoWork +=
                (o, args) => { args.Result = Updater.UpdateFirmware(this.backgroundWorker, portName, filePath); };
            this.backgroundWorker.RunWorkerCompleted += this.BackgroundWorkerOnRunWorkerCompleted;
            this.backgroundWorker.ProgressChanged +=
                (o1, changedEventArgs) => this.progress.Value = changedEventArgs.ProgressPercentage;
            this.backgroundWorker.RunWorkerAsync();
        }

        private void BackgroundWorkerOnRunWorkerCompleted(object sender,
                                                          RunWorkerCompletedEventArgs runWorkerCompletedEventArgs)
        {
            var result = (int)runWorkerCompletedEventArgs.Result;
            if (result > 0)
            {
                MessageBox.Show("Transfer Succeded. Enjoy your FEZ !");
            }
            else
            {
                string reason = result.ToString(); // todo : interpret result code
                MessageBox.Show("Transfer Failed : " + reason);
            }


            this.IsEnabled = true;
            this.canClose = true;
            this.progress.Visibility = Visibility.Hidden;
        }

        private void Window_Closing(object sender, CancelEventArgs e)
        {
            e.Cancel = !this.canClose;

            if (!e.Cancel)
            {
                if (mfDeploy != null)
                {
                    mfDeploy.Dispose();
                    mfDeploy = null;
                }
            }
        }

        private void Window_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            // NativeHelper.ExtendFrameIntoClientArea(this, -1,-1,-1,-1);
            // NativeHelper.EnableBlurBehindWindow(this);
        }

        private void BrowseClick(object sender, RoutedEventArgs e)
        {
            var dlg = new OpenFileDialog();

            bool? result = dlg.ShowDialog();

            // Get the selected file name and display in a TextBox
            if (result == true)
            {
                this.firmwareFile.Text = dlg.FileName;

                this.UpdateStatuses();
            }
        }
    }
}